package xyz.lwm.lazycat.web;

import lombok.extern.slf4j.Slf4j;
import xyz.lwm.lazycat.annotation.Destroy;
import xyz.lwm.lazycat.banner.Banner;
import xyz.lwm.lazycat.beans.AppRunner;
import xyz.lwm.lazycat.beans.SettingCustomizer;
import xyz.lwm.lazycat.event.AppFinishEvent;
import xyz.lwm.lazycat.event.Events;
import xyz.lwm.lazycat.setting.Setting;
import xyz.lwm.lazycat.setting.SettingSetter;
import xyz.lwm.lazycat.utility.It;
import xyz.lwm.lazycat.utility.NetUtil;
import xyz.lwm.lazycat.utility.PathUtil;
import xyz.lwm.lazycat.utility.StringUtil;
import xyz.lwm.lazycat.utility.resource.Location;
import xyz.lwm.lazycat.web.exception.ExceptionMapperImpl;
import xyz.lwm.lazycat.web.handle.Handler;
import xyz.lwm.lazycat.web.handle.HandlerType;
import xyz.lwm.lazycat.web.handle.ResourceHandler;
import xyz.lwm.lazycat.web.route.RouteTable;
import xyz.lwm.lazycat.web.route.RouteTableImpl;
import xyz.lwm.lazycat.web.route.RouterImpl;
import xyz.lwm.lazycat.web.settings.ProjectSetting;
import xyz.lwm.lazycat.web.settings.WebSetting;
import xyz.lwm.lazycat.web.settings.WebStaticSetting;

import java.util.Collection;

import static xyz.lwm.lazycat.utility.StrPool.STR_SLASH;

/**
 * web 启动
 *
 * @author lwm
 */
@Slf4j
public class WebAppRunner implements AppRunner, SettingCustomizer {

    // 异常处理
    private final Collection<Exceptionable> errorsCustomizers;

    // 控制器
    private final Collection<Controllable> controllables;

    // web 配置
    private final WebSetting webProperties;

    // web 服务
    private final WebServer webServer;

    // 设置属性
    private SettingSetter settingSetter;

    public WebAppRunner(Collection<Exceptionable> errorsCustomizers,
                        Collection<Controllable> controllables,
                        WebSetting webProperties,
                        WebServer webServer) {
        this.errorsCustomizers = errorsCustomizers;
        this.controllables = controllables;
        this.webProperties = webProperties;
        this.webServer = webServer;
    }

    @Override
    public void run(String[] args) {
        // 异常处理设置
        ExceptionMapperImpl exceptionMapper = new ExceptionMapperImpl();
        errorsCustomizers.forEach(c -> c.customize(exceptionMapper));

        // 路由设置
        RouteTable routeTable = new RouteTableImpl(exceptionMapper, webProperties);
        controllables.forEach(c -> c.control(new RouterImpl(routeTable)));

        // 静态资源和重定向
        WebStaticSetting webStatic = webProperties.getWebStatic();
        String contextPath = It.of(webProperties.getServer().getContextPath()).getOr("/");

        // 是否配置静态页面
        if (StringUtil.isNotBlank(webStatic.getLocation())) {
            // 静态资源处理
            String location = webStatic.getLocation();
            routeTable.add(STR_SLASH, HandlerType.STATIC, new ResourceHandler(contextPath, location));

            // 重定向处理
            It.of(contextPath)
                    .let(s -> s + STR_SLASH + webStatic.getIndex()).let(PathUtil::normalizePath)
                    .let(s -> (Handler) (ctx -> ctx.redirect(s)))
                    .also(handler -> routeTable.add(STR_SLASH, HandlerType.REDIRECT, handler))
                    .also(handler -> Events.subscribe(AppFinishEvent.class, e -> print()));
        }

        // 路由分类整理
        routeTable.classify();

        // web 服务启动
        webServer.start(routeTable);

        // 打印 ip 端口
        log.info("app started, port: {}, context-path: {}",
                webProperties.getServer().getPort(), "/" + PathUtil.normalizePath(contextPath));

    }

    private void print() {
        ProjectSetting lazycat = webProperties.getProject();
        Banner banner = It.of(lazycat.getBannerLocation()).let(Banner::new)
                .getOr(new Banner("banner.txt", Location.CLASSPATH));
        String version = It.of(lazycat.getVersion()).getOr("DEV");

        String localhost = NetUtil.getLocalhostStr();
        int port = webProperties.getServer().getPort();
        String url = StringUtil.format("http://{}:{}", localhost, port);
        settingSetter.set("lazycat.address", url);
        settingSetter.set("lazycat.version", version);
        banner.print(s -> log.info("\n" + s + "\n"));
    }

    @Destroy
    public void stop() {
        webServer.stop();
    }

    @Override
    public void customize(Setting setting, SettingSetter settingSetter) {
        this.settingSetter = settingSetter;
    }

}
